(in-package :moneychanger)

(defun parse-file (filename)
  (with-open-file (file filename)
    (parse file)))

(defun attribute-value (key node)
  (second (find key (node-attrs node)
		:key #'first :test #'string=)))

;; Courtesy of Tim Bradshaw.
(defmacro collecting (&body forms)
  ;; Collect some random stuff into a list by keeping a tail-pointer
  ;; to it, return the collected list.  No real point in using
  ;; gensyms, although one probably should on principle.
  "Collect things into a list forwards.  Within the body of this macro
   The form `(COLLECT THING)' will collect THING into the list returned by 
   COLLECTING.  Uses a tail pointer -> efficient."
  (let (($resnam$ (gensym)) ($tail$ (gensym)) ($thing$ (gensym)))
    `(let
       (,$resnam$ ,$tail$)
       (macrolet
	 ((collect
	     (thing)
	    ;; Collect returns the thing it's collecting
	    `(let ((,',$thing$ ,thing))
	       (if ,',$resnam$
		   (setf (cdr ,',$tail$)
			 (setf ,',$tail$ (list ,',$thing$)))
		   (setf ,',$resnam$
			 (setf ,',$tail$ (list ,',$thing$))))
	       ,',$thing$)))
	 ,@forms)
       ,$resnam$)))

(defun get-currencies-from-file (&optional (filename "currency.xml")
				 skip-validation)
  (if (or skip-validation
	  (validate-currency-list filename))
      (collecting
       (dolist (rate (node-children (parse-file filename)))
	 (let ((current-currency (make-instance 'currency)))
	   (dolist (attribute (node-children rate))
	     (cond ((string= (node-name attribute) "Country")
		    (setf (country current-currency)
			  (car (node-children attribute)))
		    (setf (name current-currency)
			  (attribute-value "UnitName" attribute)))
		   ((string= (node-name attribute) "Value")
		    (let ((in-usd (attribute-value "InUsd" attribute))
			  (value (read-from-string
				  (car (node-children attribute)))))
		      (setf (rate current-currency)
			    (if (string= in-usd "true")
				(/ 1.0 value)
				value))))))
	   (collect current-currency))))
      (error "Currency XML file ~A is invalid" filename)))

(defun get-currencies (&key (skip-validation t))
  (cons (make-instance 'currency
		       :name "Dollar"
		       :country "United States"
		       :rate 1.0)
	(get-currencies-from-file "currency.xml" skip-validation)))